Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Commit a729e0320fd0cdb79054d3f11ec453d86b159cc2


Parents : 9f8ffbf
Author : Ivan <ivan@quad4.io>
Signature : Signature validation error
Date : 2026-04-04T23:32:30-05:00

feat(rnpath): RNPathPage with interface discovery and improved search functionality

Changes

2 files changed, 38 insertions(+), 5 deletions(-)


Diff

diff --git a/meshchatx/src/frontend/components/tools/RNPathPage.vue b/meshchatx/src/frontend/components/tools/RNPathPage.vue
index 7acea534..6e8d7965 100644
--- a/meshchatx/src/frontend/components/tools/RNPathPage.vue
+++ b/meshchatx/src/frontend/components/tools/RNPathPage.vue
@@ -56,11 +56,13 @@
v-model="searchQuery"
type="text"
placeholder="Search Hash or Via..."
- class="input-field pl-10"
+ class="input-field pr-10"
+ autocomplete="off"
/>
<MaterialDesignIcon
icon-name="magnify"
- class="absolute left-3 top-1/2 -translate-y-1/2 size-5 text-gray-400"
+ class="pointer-events-none absolute right-3 top-1/2 size-5 -translate-y-1/2 text-gray-400"
+ aria-hidden="true"
/>
</div>
<select v-model="filterInterface" class="input-field">
@@ -361,17 +363,29 @@ export default {
async refreshAll() {
this.isLoading = true;
try {
- const [pathRes, rateRes, ifaceRes] = await Promise.all([
+ const [pathRes, rateRes, ifaceRes, discRes] = await Promise.all([
this.fetchPathTable(),
window.api.get("/api/v1/rnpath/rates"),
window.api.get("/api/v1/reticulum/interfaces"),
+ window.api.get("/api/v1/reticulum/discovered-interfaces").catch(() => ({ data: {} })),
]);
this.pathTable = pathRes.table;
this.totalItems = pathRes.total;
this.responsiveItems = pathRes.responsive;
this.unresponsiveItems = pathRes.unresponsive;
this.rateTable = rateRes.data.rates;
- this.interfaces = Object.keys(ifaceRes.data.interfaces);
+ const nameSet = new Set(Object.keys(ifaceRes.data?.interfaces || {}));
+ for (const row of discRes.data?.active || []) {
+ if (row?.name) {
+ nameSet.add(String(row.name));
+ }
+ }
+ for (const d of discRes.data?.interfaces || []) {
+ if (d && typeof d === "object" && d.name) {
+ nameSet.add(String(d.name));
+ }
+ }
+ this.interfaces = Array.from(nameSet).sort();
} catch (e) {
console.error(e);
ToastUtils.error(this.$t("tools.rnpath.failed_fetch"));

diff --git a/tests/frontend/RNPathPage.test.js b/tests/frontend/RNPathPage.test.js
index 6bc223ad..a61ff9e2 100644
--- a/tests/frontend/RNPathPage.test.js
+++ b/tests/frontend/RNPathPage.test.js
@@ -13,7 +13,7 @@ describe("RNPathPage.vue", () => {
window.api = axiosMock;
axiosMock.get.mockImplementation((url) => {
- if (url === "/api/v1/rnpath/table") {
+ if (url.startsWith("/api/v1/rnpath/table")) {
return Promise.resolve({
data: {
table: [
@@ -25,6 +25,9 @@ describe("RNPathPage.vue", () => {
expires: 1234567890,
},
],
+ total: 1,
+ responsive: 1,
+ unresponsive: 0,
},
});
}
@@ -43,6 +46,21 @@ describe("RNPathPage.vue", () => {
},
});
}
+ if (url === "/api/v1/reticulum/interfaces") {
+ return Promise.resolve({
+ data: {
+ interfaces: { UDP: {} },
+ },
+ });
+ }
+ if (url === "/api/v1/reticulum/discovered-interfaces") {
+ return Promise.resolve({
+ data: {
+ interfaces: [{ name: "Discovered-IF" }],
+ active: [{ name: "Active-IF" }],
+ },
+ });
+ }
return Promise.resolve({ data: {} });
});
});
@@ -74,6 +92,7 @@ describe("RNPathPage.vue", () => {
expect(wrapper.text()).toContain("RNPath");
expect(wrapper.vm.pathTable.length).toBe(1);
expect(wrapper.vm.rateTable.length).toBe(1);
+ expect(wrapper.vm.interfaces).toEqual(["Active-IF", "Discovered-IF", "UDP"]);
});
it("switches tabs", async () => {


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────